package com.bytz.modules.cms.order.util;

import cn.hutool.script.ScriptUtil;
import com.bytz.modules.cms.order.constant.OrderConstants;
import com.bytz.modules.cms.order.entity.Order;
import com.bytz.modules.cms.order.entity.OrderItem;
import com.bytz.modules.cms.order.model.*;

import javax.script.*;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class ScriptEvalUtil {
    /**
     * 编译的脚本缓存
     */
    private final static ConcurrentHashMap<String, ScriptCompileCache> CACHE = new ConcurrentHashMap<>();

    /**
     * 是否可编译
     */
    private final static boolean COMPILEABLE;

    /**
     * 脚本引擎
     */
    private final static ScriptEngine ENGINE;

    static {
        ENGINE = ScriptUtil.getJsEngine();

        COMPILEABLE = ENGINE instanceof Compilable;
    }


    /**
     * 创建绑定变量
     *
     * @param order      订单
     * @param orderItems 订单项
     * @return 绑定对象
     */
    public static Bindings createBinding(AdminOrderModel order, List<OrderItemModel> orderItems) {
        Bindings bindings = new SimpleBindings();
        bindings.put(OrderConstants.OrderFlowRuleBindings.ORDER, new ImmutableOrder(order));
        bindings.put(OrderConstants.OrderFlowRuleBindings.ORDER_ITEM,
                orderItems.stream().map(ImmutableOrderItem::new).collect(Collectors.toList()));

        return bindings;
    }

    /**
     * 测试脚本
     *
     * @param script   脚本内容
     * @param bindings 绑定参数
     */
    public static Object testScript(String script, Bindings bindings) throws Exception {
        return ENGINE.eval(script, bindings);
    }

    /**
     * 组合脚本
     */
    public static String combineScript(String globalScript, String privateScript) {
        return globalScript + "\r\n" + privateScript;
    }

    /**
     * 执行脚本
     *
     * @param scriptId 脚本Id
     * @param script   脚本内容
     * @param bindings 绑定参数
     */
    public static Object evalScript(String scriptId, String script, Bindings bindings) throws Exception {
        if (COMPILEABLE) {
            ScriptCompileCache scriptCompileCache = CACHE.get(scriptId);

            // 如果有缓存且脚本没有变
            if (scriptCompileCache != null && Objects.equals(script, scriptCompileCache.getScript())) {
                // 直接执行
                return scriptCompileCache.getCompiledScript().eval(bindings);
            } else {
                // 编译
                final Compilable compEngine = (Compilable) ENGINE;
                CompiledScript compiled = compEngine.compile(script);

                // 放入缓存
                ScriptCompileCache cache = new ScriptCompileCache();
                cache.setScript(script);
                cache.setCompiledScript(compiled);
                CACHE.put(scriptId, cache);

                return compiled.eval(bindings);
            }
        } else {
            // 不支持编译则直接通过engine执行
            return ENGINE.eval(script, bindings);
        }
    }
}